修饰符

函数是一种对象

Python 中,函数是也是一种对象。

In [1]:
def foo(x):
    print x
    
print(type(foo))
<type 'function'>

查看函数拥有的方法:

In [2]:
dir(foo)
Out[2]:
['__call__',
 '__class__',
 '__closure__',
 '__code__',
 '__defaults__',
 '__delattr__',
 '__dict__',
 '__doc__',
 '__format__',
 '__get__',
 '__getattribute__',
 '__globals__',
 '__hash__',
 '__init__',
 '__module__',
 '__name__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'func_closure',
 'func_code',
 'func_defaults',
 'func_dict',
 'func_doc',
 'func_globals',
 'func_name']

在这些方法中,__call__ 是最重要的一种方法:

In [3]:
foo.__call__(42)
42

相当于:

In [4]:
foo(42)
42

因为函数是对象,所以函数可以作为参数传入另一个函数:

In [5]:
def bar(f, x):
    x += 1
    f(x)
In [6]:
bar(foo, 4)
5

修饰符

修饰符是这样的一种函数,它接受一个函数作为输入,通常输出也是一个函数:

In [7]:
def dec(f):
    print 'I am decorating function', id(f)
    return f

len 函数作为参数传入这个修饰符函数:

In [8]:
declen = dec(len)
I am decorating function 33716168

使用这个新生成的函数:

In [9]:
declen([10,20,30])
Out[9]:
3

上面的例子中,我们仅仅返回了函数的本身,也可以利用这个函数生成一个新的函数,看一个新的例子:

In [10]:
def loud(f):
    def new_func(*args, **kw):
        print 'calling with', args, kw
        rtn = f(*args, **kw)
        print 'return value is', rtn
        return rtn
    return new_func
In [11]:
loudlen = loud(len)
In [12]:
loudlen([10, 20, 30])
calling with ([10, 20, 30],) {}
return value is 3
Out[12]:
3

用 @ 来使用修饰符

Python 使用 @ 符号来将某个函数替换为修饰符之后的函数:

例如这个函数:

In [13]:
def foo(x):
    print x
    
foo = dec(foo)
I am decorating function 64021672

可以替换为:

In [14]:
@dec
def foo(x):
    print x
I am decorating function 64021112

事实上,如果修饰符返回的是一个函数,那么可以链式的使用修饰符:

@dec1
@dec2
def foo(x):
    print x

使用修饰符 loud 来定义这个函数:

In [15]:
@loud
def foo(x):
    print x
In [16]:
foo(42)
calling with (42,) {}
42
return value is None

例子

定义两个修饰器函数,一个将原来的函数值加一,另一个乘二:

In [17]:
def plus_one(f):
    def new_func(x):
        return f(x) + 1
    return new_func

def times_two(f):
    def new_func(x):
        return f(x) * 2
    return new_func

定义函数,先乘二再加一:

In [18]:
@plus_one
@times_two
def foo(x):
    return int(x)
In [19]:
foo(13)
Out[19]:
27

修饰器工厂

decorators factories 是返回修饰器的函数,例如:

In [20]:
def super_dec(x, y, z):
    def dec(f):
        def new_func(*args, **kw):
            print x + y + z
            return f(*args, **kw)
        return new_func
    return dec

它的作用在于产生一个可以接受参数的修饰器,例如我们想将 loud 输出的内容写入一个文件去,可以这样做:

In [21]:
def super_loud(filename):
    fp = open(filename, 'w')
    def loud(f):
        def new_func(*args, **kw):
            fp.write('calling with' + str(args) + str(kw))
            # 确保内容被写入
            fp.flush()
            fp.close()
            rtn = f(*args, **kw)
            return rtn
        return new_func
    return loud

可以这样使用这个修饰器工厂:

In [22]:
@super_loud('test.txt')
def foo(x):
    print x

调用 foo 就会在文件中写入内容:

In [23]:
foo(12)
12

查看文件内容:

In [24]:
with open('test.txt') as fp:
    print fp.read()
calling with(12,){}
In [25]:
import os
os.remove('test.txt')